﻿/*
Module Name:  Level.cs
Project:      ScriptManager
Copyright (C) 2012 Felipe Ramos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System;

namespace ScriptManager.Scripting
{
    /// <summary>
    /// Level scriptable object. Most involve scriptable object in charge of loading level script defining regions, 
    /// effects, layers, and more.
    /// </summary>
    public class Level : IScript<LevelKeys>
    {
        private int _ticks = 0; // Keep track of level animation ticks.

        public Level(string path)
        {
            ScriptPath = path;
        }

        /// <summary>
        /// Load level script definition and embedded scriptable objects like region effects.
        /// </summary>
        /// <param name="content">ContentManager for loading assets.</param>
        public void Load(ContentManager content)
        {
            Values = new Dictionary<LevelKeys, string>(7);
            Assets = new Dictionary<string,Texture2D>(3);
            Animations = new Dictionary<LevelKeys, Animation>(1);
            AnimationPosition = new Dictionary<LevelKeys, Vector2>(1);
            Effects = new Dictionary<Rectangle, Effect>(1);

            using (var reader = new StreamReader(TitleContainer.OpenStream(ScriptPath)))
            {
                while (reader.Peek() > 0)
                {
                    string[] scriptParameters = reader.ReadLine().Split(':');
                    LevelKeys paramKey =
                        (LevelKeys)Enum.Parse(typeof(LevelKeys),scriptParameters[0].Trim().ToLower());

                    switch (paramKey)
                    {
                        case LevelKeys.title:
                        case LevelKeys.width:
                        case LevelKeys.height:
                            Values.Add(paramKey, scriptParameters[1].Trim());
                            break;
                        case LevelKeys.bg:
                        case LevelKeys.mid:
                        case LevelKeys.foreground:
                            Values.Add(paramKey, scriptParameters[1].Trim());
                            Assets.Add(scriptParameters[1].Trim(), content.Load<Texture2D>(Values[paramKey]));
                            break;
                        case LevelKeys.animation:
                            Values.Add(paramKey, scriptParameters[1].Trim());
                            Animation animation = Animation.Get(scriptParameters[1].Trim());
                            LevelKeys animationKey = (LevelKeys)Enum.Parse(typeof(LevelKeys), animation.Key, true);
                            Animations.Add(animationKey, animation);
                            AnimationPosition.Add(animationKey, Vector2.Zero);
                            break;
                        case LevelKeys.trigger:
                            string[] triggerParams = scriptParameters[1].Trim().Split(',');
                            if (triggerParams[0] == "region")
                            {
                                Rectangle region =new Rectangle(Convert.ToInt32(triggerParams[2]),
                                        Convert.ToInt32(triggerParams[3]),
                                        Convert.ToInt32(triggerParams[4]),
                                        Convert.ToInt32(triggerParams[5]));

                                Effects.Add(region, new Effect(triggerParams[1], region));
                                Effects.Last().Value.Load(content);
                            }

                            break;
                    }
                }
            }
        }

        /// <summary>
        /// Update animations and check MouseState for clicks overlapping regions.
        /// </summary>
        /// <param name="gametime">GameTime</param>
        public void Update(GameTime gametime)
        {
            _ticks += 1;

            MouseState mouseState = Mouse.GetState();

            if (Effects != null && Effects.Count > 0)
                foreach (KeyValuePair<Rectangle,Effect> effect in Effects)
                {
                    if (effect.Value.IsAlive)
                        effect.Value.Update(gametime);

                    if (mouseState.LeftButton == ButtonState.Pressed)
                    {
                        Rectangle mouseLoc = new Rectangle(mouseState.X, mouseState.Y, 1, 1);
                        if (effect.Key.Intersects(mouseLoc) && !effect.Value.IsAlive)
                            effect.Value.IsAlive = true;
                    }
                }

            if (Animations.Count > 0 && _ticks > 30)
            {
                foreach (KeyValuePair<LevelKeys, Animation> animation in Animations)
                {
                    switch (animation.Value.Axis)
                    {
                        case Axis.Y:
                            if (animation.Value.ObjectOffset > animation.Value.Offset)
                                animation.Value.ObjectOffset -= 1;
                            else
                                animation.Value.ObjectOffset = 0;

                            AnimationPosition[animation.Key] =
                                new Vector2(AnimationPosition[animation.Key].X, (float)animation.Value.ObjectOffset);
                            break;
                    }
                }

                _ticks = 0;
            }
        }

        /// <summary>
        /// Draw all layers and active effects.
        /// </summary>
        /// <param name="gameTime">GameTime</param>
        /// <param name="spriteBatch">SpriteBatch</param>
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            spriteBatch.Begin();

            // bg
            spriteBatch.Draw(Assets[Values[LevelKeys.bg]],
                Assets[Values[LevelKeys.bg]].Bounds,
                Color.White);

            // mid
            spriteBatch.Draw(Assets[Values[LevelKeys.mid]],
                Assets[Values[LevelKeys.mid]].Bounds,
                Color.White);

            // foreground
            if (Animations.ContainsKey(LevelKeys.foreground))
            {
                spriteBatch.Draw(Assets[Values[LevelKeys.foreground]],
                    AnimationPosition[LevelKeys.foreground],
                    Color.White);
            }
            else
            {
                spriteBatch.Draw(Assets[Values[LevelKeys.foreground]],
                   Assets[Values[LevelKeys.foreground]].Bounds,
                   Color.White);
            }
            spriteBatch.End();

            if (Effects != null && Effects.Count > 0)
                foreach (KeyValuePair<Rectangle, Effect> effect in Effects)
                    if (effect.Value.IsAlive)
                        effect.Value.Draw(gameTime, spriteBatch);
        }

        public Dictionary<LevelKeys, string> Values { get; set; }
        public Dictionary<LevelKeys, Animation> Animations { get; private set; }
        public Dictionary<LevelKeys, Vector2> AnimationPosition { get; private set; }
        public Dictionary<string, Texture2D> Assets { get; set; }
        public Dictionary<Rectangle, Effect> Effects { get; private set; }
        public string ScriptPath { get; set; }      
    }
}
